package com.wang.hellohibernate.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.SimpleExpression;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.wang.hellohibernate.dao.GenericDAO;

import java.lang.reflect.InvocationHandler;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public abstract class DAOSupport<T> /*extends HibernateDaoSupport*/ implements GenericDAO<T>{
	static final Logger logger = LoggerFactory.getLogger(DAOSupport.class);
	
	@Autowired
	SessionFactory sessionFactory;
	/*public void init(SessionFactory factory) {
	    setSessionFactory(factory);
	}*/

	private int pageNumber = 0;
	private int pageSize = 0;

	public int getPageNumber() {
		return pageNumber;
	}

	public void setPageNumber(int pageNumber) {
		this.pageNumber = pageNumber;
	}

	public int getPageSize() {
		return pageSize;
	}

	public void setPageSize(int pageSize) {
		this.pageSize = pageSize;
	}

	private List<SimpleExpression> expressions = null;
	private void initExpression()
	{
		if(null == expressions)
		{
			expressions = new ArrayList<SimpleExpression>();
		}
		pageNumber = 0;
	}
	
	private void clean()
	{
		expressions = null;
	}
	
	public abstract Class<T> getModelClass();
	
	public List<T> select(final T condition) {
		Session session = sessionFactory.getCurrentSession();
		Criteria criteria = session.createCriteria(getModelClass());

		final Map<String, Object> values = new HashMap<String, Object>();
		new AnnotationUtil(condition, javax.persistence.Column.class)
		{
			public void process(Annotation annotation, Field field)
			{
				String columnName = getAttributeValue(annotation, "name");
				Object value = null;
				try {
					boolean isAccessible = field.isAccessible();
					field.setAccessible(true);
					value = field.get(condition);
					field.setAccessible(isAccessible);
				} catch (Exception e) {
					logger.error(e.getMessage());
					throw new RuntimeException(e);
				}
				
				if(null != value)
				{
					values.put(columnName, value);
				}
			}
		};
		criteria.add(Restrictions.allEq(values));

		List<T> result = list(criteria);
		clean();
		
		return result;
	}
	
	public T selectById(Integer id)
	{
		T daoObject = null;
		try {
			daoObject = getModelClass().newInstance();
			setIdentifier(daoObject, id);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw new RuntimeException("No identifier speciafied");
		}

		List<T> objectList = (List<T>) select((T)daoObject);
		if(null == objectList || objectList.size() == 0)
			return null;
		else
			return objectList.get(0);
	}
	
	public List<T> select()
	{
		Session session = sessionFactory.getCurrentSession();
		Criteria criteria = session.createCriteria(getModelClass());
		
		if(null != expressions)
		{
			for(SimpleExpression expression: expressions)
			{
				criteria.add(expression);
			}
		}

		List<T> result = list(criteria);
		clean();
	
		return result;
	}
	
	@SuppressWarnings("unchecked")
	private List<T> list(Criteria criteria)
	{
		if(pageSize > 0)
		{
			criteria.setMaxResults(pageSize);
			criteria.setFirstResult(pageSize * pageNumber + pageSize);
		}
		List<T> results = criteria.list();
		if((null != results || (results.size() == pageSize)) && pageSize > 0)
		{
			pageNumber++;
		}
		
		return results;
	}
	
	public void setEquals(String columnName, Object value)
	{
		initExpression();
		SimpleExpression expression = Restrictions.eq(columnName, value);
		expressions.add(expression);
	}
	
	public void setLikes(String columnName, Object value)
	{
		initExpression();
		SimpleExpression expression = null;
		if(value instanceof String)
			expression = Restrictions.like(columnName, (String)value, MatchMode.ANYWHERE);
		else
			expression = Restrictions.like(columnName, value);
		expressions.add(expression);
	}

	public static <T> void setIdentifier(final T entity, final Integer identifier)
	{
		new AnnotationUtil(entity, javax.persistence.Id.class)
		{
			public void process(Annotation annotation, Field field)
			{
				try {
					boolean isAccessible = field.isAccessible();
					field.setAccessible(true);
					field.set(entity, identifier);
					field.setAccessible(isAccessible);
				} catch (Exception e) {
					logger.error(e.getMessage());
					throw new RuntimeException(e);
				}
			}
		};
	}
}
